'MTU': 1500,
}
+console_cfg = {
+ 'protocol': 'rfb',
+ 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'},
+}
+
+
import sys
import time
sys.path.append('/usr/lib/python')
vif_cfg['VM'] = vm_uuid
vif_uuid = execute(server, 'VIF.create', (session, vif_cfg))
+ # Create a console
+ console_cfg['VM'] = vm_uuid
+ console_uuid = execute(server, 'console.create',
+ (session, console_cfg))
+ print console_uuid
+
# Start the VM
execute(server, 'VM.start', (session, vm_uuid, False))
'network': '',
'MAC': '',
'MTU': 1500,
+}
+
+console_cfg = {
+ 'protocol': 'rfb',
+ 'other_config': {'vncunused': 1, 'vncpasswd': 'testing'},
}
import sys
vif_cfg['VM'] = vm_uuid
vif_uuid = execute(server, 'VIF.create', (session, vif_cfg))
+ # Create a console
+ console_cfg['VM'] = vm_uuid
+ console_uuid = execute(server, 'console.create',
+ (session, console_cfg))
+ print console_uuid
+
# Start the VM
execute(server, 'VM.start', (session, vm_uuid, False))
time.sleep(30)
- test_suspend = True
+ test_suspend = False
if test_suspend:
print 'Suspending VM..'
execute(server, 'VM.suspend', (session, vm_uuid))
VBD_LIST_FORMAT = '%(device)-6s %(uuid)-36s %(VDI)-8s'
TASK_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(status)-8s %(progress)-4s'
VIF_LIST_FORMAT = '%(name)-8s %(device)-7s %(uuid)-36s %(MAC)-10s'
-CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(uri)-32s'
+CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(location)-32s'
COMMANDS = {
'host-info': ('', 'Get Xen Host Info'),
if not is_long:
print CONSOLE_LIST_FORMAT % {'protocol': 'Protocol',
- 'uri': 'URI',
+ 'location': 'Location',
'uuid': 'UUID'}
for console in consoles:
# ----------------------------------------------------------------
- console_attr_ro = ['uri', 'protocol', 'VM']
- console_attr_rw = []
+ console_attr_ro = ['location', 'protocol', 'VM']
+ console_attr_rw = ['other_config']
+ console_funcs = [('create', 'console')]
def console_get_all(self, session):
xendom = XendDomain.instance()
cons = reduce(lambda x, y: x + y, cons)
return xen_api_success(cons)
- def console_get_uri(self, session, console_ref):
+ def console_get_location(self, session, console_ref):
return xen_api_success(xendom.get_dev_property_by_uuid('console',
console_ref,
- 'uri'))
+ 'location'))
def console_get_protocol(self, session, console_ref):
return xen_api_success(xendom.get_dev_property_by_uuid('console',
return xen_api_success(return_cfg)
+ def console_create(self, session, console_struct):
+ xendom = XendDomain.instance()
+ if not xendom.is_valid_vm(console_struct['VM']):
+ return xen_api_error(['VM_HANDLE_INVALID', console_struct['VM']])
+
+ dom = xendom.get_vm_by_uuid(console_struct['VM'])
+ try:
+ if 'protocol' not in console_struct:
+ return xen_api_error(['CONSOLE_PROTOCOL_INVALID',
+ 'No protocol specified'])
+
+ console_ref = dom.create_console(console_struct)
+ xendom.managed_config_save(dom)
+ return xen_api_success(console_ref)
+ except XendError, e:
+ return xen_api_error([XEND_ERROR_TODO, str(e)])
# Xen API: Class SR
# ----------------------------------------------------------------
'platform_serial' : 'serial',
'platform_localtime': 'localtime',
'platform_keymap' : 'keymap',
+ 'HVM_boot': 'boot',
}
# List of XendConfig configuration keys that have no direct equivalent
('sdl', int),
('vncdisplay', int),
('vncunused', int),
- ('vncpasswd', str),
+ ('vncpasswd', str),
+ ('vnclisten', str),
]
LEGACY_IMAGE_HVM_CFG = [
'vif_refs': [],
'vbd_refs': [],
'vtpm_refs': [],
+ 'other_config': {},
}
defaults['name_label'] = 'Domain-' + defaults['uuid']
self['vbd_refs'] = cfg.get('vbd_refs', [])
self['vtpm_refs'] = cfg.get('vtpm_refs', [])
+ # coalesce hvm vnc frame buffer with vfb config
+ if self['image']['type'] == 'hvm' and self['image'].get('vnc', 0):
+ # add vfb device if it isn't there already
+ has_rfb = False
+ for console_uuid in self['console_refs']:
+ if self['devices'][console_uuid][1].get('protocol') == 'rfb':
+ has_rfb = True
+ break
+
+ if not has_rfb:
+ dev_config = ['vfb']
+ # copy VNC related params from image config to vfb dev conf
+ for key in ['vncpasswd', 'vncunused', 'vncdisplay',
+ 'vnclisten']:
+ if key in self['image']:
+ dev_config.append([key, self['image'][key]])
+
+ self.device_add('vfb', cfg_sxp = dev_config)
+
def _sxp_to_xapi_unsupported(self, sxp_cfg):
"""Read in an SXP configuration object and populate
# currently unsupported options
self['image']['hvm']['device_model'] = LEGACY_DM
- self['image']['vnc'] = 1
+ self['image']['vnc'] = 0
self['image']['hvm']['pae'] = 1
if self['platform_enable_audio']:
# store as part of the device config.
dev_uuid = sxp.child_value(config, 'uuid')
dev_type, dev_cfg = self['devices'][dev_uuid]
- config.append(['bootable',
- int(dev_cfg['bootable'])])
+ is_bootable = dev_cfg.get('bootable', False)
+ config.append(['bootable', int(is_bootable)])
+
sxpr.append(['device', config])
found = True
# bootable.
dev_info['bootable'] = True
target['vbd_refs'].append(dev_uuid)
+
+ elif dev_type == 'vfb':
+ # Populate other config with aux data that is associated
+ # with vfb
+
+ other_config = {}
+ for key in ['vncunused', 'vncdisplay', 'vnclisten',
+ 'vncpasswd', 'type', 'display', 'xauthority',
+ 'keymap']:
+ if key in dev_info:
+ other_config[key] = dev_info[key]
+ target['devices'][dev_uuid][1]['other_config'] = other_config
+
+
+ if 'console_refs' not in target:
+ target['console_refs'] = []
+
+ # Treat VFB devices as console devices so they are found
+ # through Xen API
+ if dev_uuid not in target['console_refs']:
+ target['console_refs'].append(dev_uuid)
+
elif dev_type == 'console':
if 'console_refs' not in target:
target['console_refs'] = []
target['devices'][dev_uuid] = (dev_type, dev_info)
target['vtpm_refs'].append(dev_uuid)
+ elif dev_type == 'console':
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
+ dev_info['other_config'] = cfg_xenapi.get('other_config', {})
+ if dev_info['protocol'] == 'rfb':
+ # collapse other config into devinfo for things
+ # such as vncpasswd, vncunused, etc.
+ dev_info.update(cfg_xenapi.get('other_config', {}))
+ dev_info['type'] = 'vnc'
+ target['devices'][dev_uuid] = ('vfb', dev_info)
+ target['console_refs'].append(dev_uuid)
+
+ # Finally, if we are a pvfb, we need to make a vkbd
+ # as well that is not really exposed to Xen API
+ vkbd_uuid = uuid.createString()
+ target['devices'][vkbd_uuid] = ('vkbd', {})
+
+ elif dev_info['protocol'] == 'vt100':
+ # if someone tries to create a VT100 console
+ # via the Xen API, we'll have to ignore it
+ # because we create one automatically in
+ # XendDomainInfo._update_consoles
+ raise XendConfigError('Creating vt100 consoles via '
+ 'Xen API is unsupported')
+
return dev_uuid
# no valid device to add
return ''
- def console_add(self, protocol, uri):
+ def console_add(self, protocol, location, other_config = {}):
dev_uuid = uuid.createString()
- dev_info = {
- 'uuid': dev_uuid,
- 'protocol': protocol,
- 'uri': uri
- }
- if 'devices' not in self:
- self['devices'] = {}
+ if protocol == 'vt100':
+ dev_info = {
+ 'uuid': dev_uuid,
+ 'protocol': protocol,
+ 'location': location,
+ 'other_config': other_config,
+ }
+
+ if 'devices' not in self:
+ self['devices'] = {}
- self['devices'][dev_uuid] = ('console', dev_info)
- self['console_refs'].append(dev_uuid)
- return dev_info
+ self['devices'][dev_uuid] = ('console', dev_info)
+ self['console_refs'].append(dev_uuid)
+ return dev_info
+
+ return {}
+
+ def console_update(self, console_uuid, key, value):
+ for dev_uuid, (dev_type, dev_info) in self['devices'].items():
+ if dev_uuid == console_uuid:
+ dev_info[key] = value
+ break
def console_get_all(self, protocol):
- consoles = [dinfo for dtype, dinfo in self['devices'].values()
- if dtype == 'console']
- return [c for c in consoles if c.get('protocol') == protocol]
+ if protocol == 'vt100':
+ consoles = [dinfo for dtype, dinfo in self['devices'].values()
+ if dtype == 'console']
+ return [c for c in consoles if c.get('protocol') == protocol]
+
+ elif protocol == 'rfb':
+ vfbs = [dinfo for dtype, dinfo in self['devices'].values()
+ if dtype == 'vfb']
+
+ # move all non-console key values to other_config before
+ # returning console config
+ valid_keys = ['uuid', 'location']
+ for vfb in vfbs:
+ other_config = {}
+ for key, val in vfb.items():
+ if key not in valid_keys:
+ other_config[key] = vfb[key]
+ del vfb[key]
+ vfb['other_config'] = other_config
+ vfb['protocol'] = 'rfb'
+
+ return vfbs
+
+ else:
+ return []
- def device_update(self, dev_uuid, cfg_sxp):
+ def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
"""Update an existing device with the new configuration.
@rtype: boolean
@return: Returns True if succesfully found and updated a device conf
"""
- if dev_uuid in self['devices']:
+ if dev_uuid in self['devices'] and cfg_sxp:
if sxp.child0(cfg_sxp) == 'device':
config = sxp.child0(cfg_sxp)
else:
config = cfg_sxp
-
+
+ dev_type, dev_info = self['devices'][dev_uuid]
for opt_val in config[1:]:
try:
opt, val = opt_val
- self['devices'][dev_uuid][opt] = val
+ dev_info[opt] = val
except (TypeError, ValueError):
pass # no value for this config option
-
+
+ self['devices'][dev_uuid] = (dev_type, dev_info)
return True
+
+ elif dev_uuid in self['devices'] and cfg_xenapi:
+ dev_type, dev_info = self['devices'][dev_uuid]
+ for key, val in cfg_xenapi.items():
+ dev_info[key] = val
+ self['devices'][dev_uuid] = (dev_type, dev_info)
return False
if not serial_consoles:
cfg = self.info.console_add('vt100', self.console_port)
self._createDevice('console', cfg)
+ else:
+ console_uuid = serial_consoles[0].get('uuid')
+ self.info.console_update(console_uuid, 'location',
+ self.console_port)
+
- # Update VNC port if it exists
+ # Update VNC port if it exists and write to xenstore
vnc_port = self.readDom('console/vnc-port')
if vnc_port is not None:
- vnc_consoles = self.info.console_get_all('rfb')
- if not vnc_consoles:
- cfg = self.info.console_add('rfb', 'localhost:%s' %
- str(vnc_port))
- self._createDevice('console', cfg)
-
+ for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
+ if dev_type == 'vfb':
+ old_location = dev_info.get('location')
+ listen_host = dev_info.get('vnclisten', 'localhost')
+ new_location = '%s:%s' % (listen_host, str(vnc_port))
+ if old_location == new_location:
+ break
+
+ dev_info['location'] = new_location
+ self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
+ vfb_ctrl = self.getDeviceController('vfb')
+ vfb_ctrl.reconfigureDevice(0, dev_info)
+ break
+
#
# Function to update xenstore /vm/*
#
@rtype: dictionary
"""
- dev_type_config = self.info['devices'].get(dev_uuid)
+ dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
# shortcut if the domain isn't started because
# the devcontrollers will have no better information
# than XendConfig.
if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
- if dev_type_config:
- return copy.deepcopy(dev_type_config[1])
+ if dev_config:
+ return copy.deepcopy(dev_config)
return None
# instead of using dev_class, we use the dev_type
# that is from XendConfig.
- # This will accomdate 'tap' as well as 'vbd'
- dev_type = dev_type_config[0]
-
controller = self.getDeviceController(dev_type)
if not controller:
return None
if not all_configs:
return None
- dev_config = copy.deepcopy(dev_type_config[1])
+ updated_dev_config = copy.deepcopy(dev_config)
for _devid, _devcfg in all_configs.items():
if _devcfg.get('uuid') == dev_uuid:
- dev_config.update(_devcfg)
- dev_config['id'] = _devid
- return dev_config
+ updated_dev_config.update(_devcfg)
+ updated_dev_config['id'] = _devid
+ return updated_dev_config
- return dev_config
+ return updated_dev_config
def get_dev_xenapi_config(self, dev_class, dev_uuid):
config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
return dev_uuid
+ def create_console(self, xenapi_console):
+ """ Create a console device from a Xen API struct.
+
+ @return: uuid of device
+ @rtype: string
+ """
+ if self.state not in (DOM_STATE_HALTED,):
+ raise VmError("Can only add console to a halted domain.")
+
+ dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
+ if not dev_uuid:
+ raise XendError('Failed to create device')
+
+ return dev_uuid
+
def destroy_device_by_uuid(self, dev_type, dev_uuid):
if dev_uuid not in self.info['devices']:
raise XendError('Device does not exist')
from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS
from xen.xend.XendError import VmError, XendError
from xen.xend.XendLogging import log
+from xen.xend.XendOptions import instance as xenopts
from xen.xend.server.netif import randomMAC
from xen.xend.xenstore.xswatch import xswatch
from xen.xend import arch
self.pid = None
- self.dmargs += self.configVNC(imageConfig)
-
self.pae = imageConfig['hvm'].get('pae', 0)
self.apic = imageConfig['hvm'].get('apic', 0)
self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
'localtime', 'serial', 'stdvga', 'isa',
'acpi', 'usb', 'usbdevice', 'keymap' ]
+
hvmDeviceConfig = vmConfig['image']['hvm']['devices']
-
ret = ['-vcpus', str(self.vm.getVCpuCount())]
for a in dmargs:
ret.append("-net")
ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
- return ret
-
- def configVNC(self, imageConfig):
- # Handle graphics library related options
- vnc = imageConfig.get('vnc')
- sdl = imageConfig.get('sdl')
- ret = []
- nographic = imageConfig.get('nographic')
- # get password from VM config (if password omitted, None)
- vncpasswd_vmconfig = imageConfig.get('vncpasswd')
+ #
+ # Find RFB console device, and if it exists, make QEMU enable
+ # the VNC console.
+ #
+ if vmConfig['image'].get('nographic'):
+ # skip vnc init if nographic is set
+ ret.append('-nographic')
+ return ret
- if nographic:
+ vnc_config = {}
+ has_vfb = False
+ has_vnc = int(vmConfig['image'].get('vnc')) != 0
+ for dev_uuid in vmConfig['console_refs']:
+ dev_type, dev_info = vmConfig['devices'][devuuid]
+ if dev_type == 'rfb':
+ vnc_config = dev_info.get('other_config', {})
+ has_vfb = True
+ break
+
+ if not vnc_config:
+ for key in ('vncunused', 'vnclisten', 'vncdisplay', 'vncpasswd'):
+ if key in vmConfig['image']:
+ vnc_config[key] = vmConfig['image'][key]
+
+ if not has_vfb and not has_vnc:
ret.append('-nographic')
return ret
- if vnc:
- vncdisplay = imageConfig.get('vncdisplay',
- int(self.vm.getDomid()))
- vncunused = imageConfig.get('vncunused')
+
+ if not vnc_config.get('vncunused', 0) and \
+ vnc_config.get('vncdisplay', 0):
+ ret.append('-vnc')
+ ret.append(str(vncdisplay))
+ else:
+ ret.append('-vncunused')
- if vncunused:
- ret += ['-vncunused']
- else:
- ret += ['-vnc', '%d' % vncdisplay]
-
- vnclisten = imageConfig.get('vnclisten')
-
- if not(vnclisten):
- vnclisten = (xen.xend.XendOptions.instance().
- get_vnclisten_address())
- if vnclisten:
- ret += ['-vnclisten', vnclisten]
-
- vncpasswd = vncpasswd_vmconfig
- if vncpasswd is None:
- vncpasswd = (xen.xend.XendOptions.instance().
- get_vncpasswd_default())
- if vncpasswd is None:
- raise VmError('vncpasswd is not set up in ' +
- 'VMconfig and xend-config.')
- if vncpasswd != '':
- self.vm.storeVm("vncpasswd", vncpasswd)
+ vnclisten = vnc_config.get('vnclisten',
+ xenopts().get_vnclisten_address())
+ ret.append('-vnclisten')
+ ret.append(str(vnclisten))
+
+ # Store vncpassword in xenstore
+ vncpasswd = vnc_config.get('vncpasswd')
+ if not vncpasswd:
+ vncpasswd = xenopts().get_vncpasswd_default()
+
+ if vncpasswd is None:
+ raise VmError('vncpasswd is not setup in vmconfig or '
+ 'xend-config.sxp')
+
+ if vncpasswd != '':
+ self.vm.storeVm('vncpasswd', vncpasswd)
return ret
console devices with persistent UUIDs.
"""
- valid_cfg = ['uri', 'uuid', 'protocol']
+ valid_cfg = ['location', 'uuid', 'protocol']
def __init__(self, vm):
DevController.__init__(self, vm)
def migrate(self, deviceConfig, network, dst, step, domName):
return 0
+
+ def destroyDevice(self, devid, force):
+ DevController.destroyDevice(self, devid, True)
+
os.waitpid(p, 0)
CONFIG_ENTRIES = ['type', 'vncdisplay', 'vnclisten', 'vncpasswd', 'vncunused',
- 'display', 'xauthority', 'keymap' ]
+ 'display', 'xauthority', 'keymap',
+ 'uuid', 'location', 'protocol']
class VfbifController(DevController):
"""Virtual frame buffer controller. Handles all vfb devices for a domain.
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
- back = dict([(k, config[k]) for k in CONFIG_ENTRIES
+ back = dict([(k, str(config[k])) for k in CONFIG_ENTRIES
if config.has_key(k)])
- return (0, back, {})
+ devid = 0
+ return (devid, back, {})
def getDeviceConfiguration(self, devid):
def createDevice(self, config):
DevController.createDevice(self, config)
+ if self.vm.info.get('HVM_boot'):
+ # is HVM, so qemu-dm will handle the vfb.
+ return
+
std_args = [ "--domid", "%d" % self.vm.getDomid(),
"--title", self.vm.getName() ]
t = config.get("type", None)
else:
raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
+
+ def waitForDevice(self, devid):
+ if self.vm.info.get('HVM_boot'):
+ log.debug('skip waiting for HVM vfb')
+ # is a qemu-dm managed device, don't wait for hotplug for these.
+ return
+
+ DevController.waitForDevice(self, devid)
+
+
+ def reconfigureDevice(self, _, config):
+ """ Only allow appending location information of vnc port into
+ xenstore."""
+
+ if 'location' in config:
+ (devid, back, front) = self.getDeviceDetails(config)
+ self.writeBackend(devid, 'location', config['location'])
+ return back.get('uuid')
+
+ raise VmError('Refusing to reconfigure device vfb:%d' % devid)
+
+ def destroyDevice(self, devid, force):
+ if self.vm.info.get('HVM_boot'):
+ # remove the backend xenstore entries for HVM guests no matter
+ # what
+ DevController.destroyDevice(self, devid, True)
+ else:
+ DevController.destroyDevice(self, devid, force)
+
+
+ def migrate(self, deviceConfig, network, dst, step, domName):
+ if self.vm.info.get('HVM_boot'):
+ return 0
+ return DevController.migrate(self, deviceConfig, network, dst, step,
+ domName)
+
class VkbdifController(DevController):
"""Virtual keyboard controller. Handles all vkbd devices for a domain.
"""
back = {}
front = {}
return (devid, back, front)
+
+ def waitForDevice(self, config):
+ if self.vm.info.get('HVM_boot'):
+ # is a qemu-dm managed device, don't wait for hotplug for these.
+ return
+
+ DevController.waitForDevice(self, config)
+
+ def destroyDevice(self, devid, force):
+ if self.vm.info.get('HVM_boot'):
+ # remove the backend xenstore entries for HVM guests no matter
+ # what
+ DevController.destroyDevice(self, devid, True)
+ else:
+ DevController.destroyDevice(self, devid, force)
+
+ def migrate(self, deviceConfig, network, dst, step, domName):
+ if self.vm.info.get('HVM_boot'):
+ return 0
+ return DevController.migrate(self, deviceConfig, network, dst, step,
+ domName)